refactor/KD-28 File 및 Carousel 도메인 엔티티 분리#269
Conversation
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the Walkthrough파일 도메인 전반을 FileEntity에서 FileModel로 전환하고, JPA 지속성은 별도 JpaEntity로 분리했습니다. 파일 인프라 패키지 구조를 repository/ entity/ properties로 재배치했습니다. 캐러셀 도메인은 순수 모델로 변경하고 JPA 어댑터를 도입했습니다. 관련 서비스/응답/테스트 코드를 모두 새로운 타입과 패스로 정리했습니다. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Test Coverage Report
Files
|
Codecov Report❌ Patch coverage is
@@ Coverage Diff @@
## develop #269 +/- ##
=============================================
- Coverage 93.51% 93.29% -0.23%
Complexity 163 163
=============================================
Files 49 49
Lines 478 477 -1
Branches 4 4
=============================================
- Hits 447 445 -2
Misses 26 26
- Partials 5 6 +1
Continue to review full report in Codecov by Sentry.
🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
aics-domain/src/main/java/kgu/developers/domain/carousel/application/command/CarouselCommandService.java (1)
24-29: 도메인 분리 이후 변경사항이 영속되지 않음 (머지 전 필수 수정)Carousel이 이제 순수 도메인이므로 변경 감지가 되지 않습니다. update 후 repository.save(...)를 호출하지 않으면 DB에 반영되지 않습니다.
@Transactional public void updateCarousel(Carousel carousel, String link, String text, Long fileId) { carousel.updateText(text); carousel.updateLink(link); carousel.updateFileId(fileId); + carouselRepository.save(carousel); }aics-domain/src/main/java/kgu/developers/domain/file/infrastructure/repository/FileStorageServiceImpl.java (3)
78-88: loadAsResource 경로 조작(Path Traversal) 취약점 (머지 전 필수 수정)외부 입력 fileName을 normalize만 하고 루트 범위 검증을 하지 않습니다. rootLocation 밖으로 이탈이 가능합니다.
@Override public Resource loadAsResource(String fileName) { - try { - Path filePath = rootLocation.resolve(fileName).normalize(); - Resource resource = new UrlResource(filePath.toUri()); - if (!resource.exists()) throw new FileNotFoundException(); - return resource; - } catch (MalformedURLException | FileNotFoundException e) { - throw new FileNotFoundException(); - } + try { + validateInvalidPath(fileName); + Path filePath = rootLocation.resolve(fileName).normalize(); + if (!filePath.startsWith(rootLocation)) { + throw new FilePathInvalidException(); + } + Resource resource = new UrlResource(filePath.toUri()); + if (!resource.exists()) throw new FileNotFoundException(); + return resource; + } catch (MalformedURLException e) { + throw new FileNotFoundException(); + } }
56-76: store에서 원본 파일명 NPE가 커스텀 예외로 래핑되지 않음Objects.requireNonNull(...)이 try 바깥에 있어 NullPointerException이 그대로 전파됩니다. try 내부로 이동시켜 FileStoreFailedException으로 일관되게 래핑하세요.
@Override public String store(MultipartFile file, FileDomain fileDomain) { - String fileName = StringUtils.cleanPath(Objects.requireNonNull(file.getOriginalFilename())); - String path = getFullPath(fileDomain, fileName); - validateAttributes(path, fileName); - try { + try { + String fileName = StringUtils.cleanPath(Objects.requireNonNull(file.getOriginalFilename())); + String path = getFullPath(fileDomain, fileName); + validateAttributes(path, fileName); Path targetLocation = this.rootLocation.resolve(path); Files.copy(file.getInputStream(), targetLocation, REPLACE_EXISTING); File originalFile = targetLocation.toFile(); String contentType = file.getContentType(); if (contentType != null && contentType.startsWith(IMAGE_CONTENT_TYPE_PREFIX)) { imageResizingService.imageResize(originalFile, 800, 600, 1); } String relativePath = this.rootLocation.relativize(originalFile.toPath()).toString(); return url + "/" + relativePath.replace(File.separator, "/"); } catch (Exception e) { throw new FileStoreFailedException(); } }
123-125: 유효하지 않은 경로 검증 강화절대 경로 여부도 함께 차단해야 합니다.
- private static void validateInvalidPath(String fileName) throws FilePathInvalidException { - if(fileName.contains("..")) throw new FilePathInvalidException(); - } + private static void validateInvalidPath(String fileName) throws FilePathInvalidException { + java.nio.file.Path p = java.nio.file.Paths.get(fileName); + if (p.isAbsolute() || fileName.contains("..")) throw new FilePathInvalidException(); + }
🧹 Nitpick comments (17)
aics-domain/src/main/java/kgu/developers/domain/carousel/domain/Carousel.java (1)
10-17: 도메인 모델로의 전환이 적절하지만, 설계 개선이 필요합니다.JPA 엔티티에서 순수 도메인 모델로의 전환은 좋은 방향입니다. 다만 몇 가지 개선 사항이 있습니다:
id필드가final인데 생성 시null을 허용하는 구조가 다소 부자연스럽습니다.- 시간 관련 필드들이 가변적이어서 도메인 모델의 불변성 원칙에 어긋납니다.
다음과 같이 개선을 고려해보세요:
@Getter @AllArgsConstructor public class Carousel { private final Long id; private String text; private String link; private Long fileId; private final LocalDateTime createdAt; private final LocalDateTime updatedAt; private final LocalDateTime deletedAt; // 새 캐러셀 생성용 팩토리 메소드 public static Carousel create(String text, String link, Long fileId) { LocalDateTime now = LocalDateTime.now(); return new Carousel(null, text, link, fileId, now, now, null); } // 업데이트된 캐러셀 반환 (불변성 유지) public Carousel updateText(String text) { return new Carousel(this.id, text, this.link, this.fileId, this.createdAt, LocalDateTime.now(), this.deletedAt); } }aics-domain/src/main/java/kgu/developers/domain/file/domain/FileModel.java (1)
21-23: 상수 정의를 별도 클래스로 분리하는 것을 고려해보세요.파일 크기 관련 상수들을
FileSizeConstants같은 별도 클래스로 분리하면 재사용성이 높아집니다.public final class FileSizeConstants { public static final long KB = 1024L; public static final long MB = KB * 1024; public static final long GB = MB * 1024; private FileSizeConstants() {} }aics-domain/src/main/java/kgu/developers/domain/carousel/infrastructure/entity/CarouselJpaEntity.java (2)
27-35: null 제약과 도메인 불변성 정합성 확인 필요@column(nullable = false) 제약과 도메인(Carousel.update*)이 null 값을 허용할 가능성이 엇갈릴 수 있습니다. 도메인 계층에서 null 방지를 보장하거나 매퍼(toEntity)에서 방어 로직을 추가해 JPA 레벨에서 예외가 터지지 않도록 해주세요.
36-43: 매핑 메서드 네이밍 일관성 제안정적 toEntity(...) vs 인스턴스 toDomain()은 대칭성이 떨어집니다. fromDomain(...) / toDomain() 또는 toEntity() / fromEntity() 식으로 맞추면 가독성이 좋아집니다.
aics-domain/src/main/java/kgu/developers/domain/carousel/application/command/CarouselCommandService.java (1)
24-25: 파라미터 순서 가독성update 시그니처가 (link, text, fileId) 순서인데 create는 (fileId, text, link) 흐름입니다. 텍스트/링크 순서를 통일하면 혼동을 줄일 수 있습니다.
aics-domain/src/main/java/kgu/developers/domain/file/application/command/FileCommandService.java (1)
15-19: 변수명 오해 소지 및 원본 파일명 null 처리
- 변수명이 fileEntity이지만 타입은 FileModel입니다. 혼동을 줄이기 위해 rename 권장.
- MultipartFile.getOriginalFilename()이 null일 수 있습니다. FileModel.create에서 방어하거나 여기서 체크하세요.
- public FileModel saveFile(MultipartFile file, String storedPath) { - FileModel fileEntity = FileModel.create(file.getOriginalFilename(), storedPath, file.getSize(), - file.getContentType()); - return fileRepository.save(fileEntity); - } + public FileModel saveFile(MultipartFile file, String storedPath) { + FileModel fileModel = FileModel.create(file.getOriginalFilename(), storedPath, file.getSize(), + file.getContentType()); + return fileRepository.save(fileModel); + }aics-domain/src/main/java/kgu/developers/domain/file/application/query/FileQueryService.java (2)
17-19: null 반환 대신 예외/Optional 사용 권장현재 null을 반환해 상위 계층에서 NPE 위험이 있습니다. 도메인 예외를 던지거나 Optional을 반환하도록 일관 정책을 정하는 편이 안전합니다. 예: 예외 방식
-import kgu.developers.domain.file.domain.FileModel; +import kgu.developers.domain.file.domain.FileModel; +import kgu.developers.domain.file.exception.FileNotFoundException; - public FileModel getFileById(Long id) { - return fileRepository.findById(id).orElse(null); - } + public FileModel getFileById(Long id) { + return fileRepository.findById(id).orElseThrow(FileNotFoundException::new); + }
21-25: toMap 충돌 가능성 점검ID 중복이 절대 발생하지 않는다는 전제가 맞는지 확인 부탁드립니다. 중복 시 Collectors.toMap이 IllegalStateException을 던집니다.
aics-domain/src/main/java/kgu/developers/domain/carousel/infrastructure/CarouselRepositoryImpl.java (1)
13-16: 읽기/쓰기 트랜잭션 구분 권장 (@transactional 적용).읽기 메서드는 readOnly로, 쓰기/삭제는 기본 트랜잭션으로 명시하면 JPA flush/더티체킹 비용을 줄이고 실수로 인한 쓰기를 방지할 수 있습니다.
다음 변경을 제안합니다:
+import org.springframework.transaction.annotation.Transactional; -@Repository -@RequiredArgsConstructor -public class CarouselRepositoryImpl implements CarouselRepository { +@Repository +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class CarouselRepositoryImpl implements CarouselRepository { @@ - @Override - public Carousel save(Carousel carousel) { + @Override + @Transactional + public Carousel save(Carousel carousel) { @@ - @Override - public void deleteById(Long id) { + @Override + @Transactional + public void deleteById(Long id) {Also applies to: 18-23, 25-28
aics-domain/src/main/java/kgu/developers/domain/file/infrastructure/repository/QueryFileRepository.java (1)
13-16: readOnly 트랜잭션으로 조회 최적화.조회 전용 리포지토리이므로 readOnly를 명시하면 영속성 컨텍스트 쓰기 오버헤드를 줄일 수 있습니다.
+import org.springframework.transaction.annotation.Transactional; @@ -@Repository +@Repository +@Transactional(readOnly = true) public class QueryFileRepository {Also applies to: 18-26
aics-domain/src/testFixtures/java/carousel/application/CarouselCommandServiceTest.java (1)
62-69: 테스트 설명 문자열 오타 수정.DisplayName의 "upateCarousel" → "updateCarousel".
- @DisplayName("upateCarousel은 캐러셀을 수정한다.") + @DisplayName("updateCarousel은 캐러셀을 수정한다.")aics-domain/src/main/java/kgu/developers/domain/file/infrastructure/entity/FileJpaEntity.java (2)
27-37: 암호문 길이 대비 컬럼 길이 지정 권장.logicalName/physicalPath가 암호화(예: Base64)되면 기본 길이(255) 초과 가능성이 있습니다. 명시적 길이를 부여하세요.
-@Column(nullable = false) +@Column(nullable = false, length = 512) private String logicalName; -@Column(nullable = false, unique = true) +@Column(nullable = false, unique = true, length = 1024) private String physicalPath; -@Column(nullable = false) +@Column(nullable = false, length = 32) // "12.34 MB" 등 충분한 범위 private String fileSize; -@Column(nullable = false) +@Column(nullable = false, length = 50) // 확장자/MIME 표기 범주 고려 private String extension;
33-37: fileSize를 숫자+단위 분리 저장 고려.표시용 문자열만 저장하면 정렬/범위쿼리가 어렵습니다. 바이트(Long) 컬럼을 추가해 정렬/검색을 지원하고, 표시 문자열은 파생 값으로 처리하는 것을 권장합니다.
aics-domain/src/main/java/kgu/developers/domain/file/domain/FileRepository.java (1)
9-12: 파라미터 명칭 정합성 및 계약 명세화.
- save의 파라미터 이름
fileEntity→file로 변경하여 타입과 일치시키세요.- findPhysicalPathById의 반환이 “복호화된 경로”인지 계약을 Javadoc으로 명시해 주세요(상위 코멘트 참조).
- FileModel save(FileModel fileEntity); + FileModel save(FileModel file);aics-domain/src/testFixtures/java/mock/repository/FakeFileRepository.java (2)
11-11: 동시성 스트림 사용 주의 또는 자료구조 교체 제안Collections.synchronizedList는 반복/스트림 시 외부 동기화가 필요합니다. 멀티스레드 테스트 가능성이 있으면 synchronized(data)로 감싸거나 CopyOnWriteArrayList로 교체하세요. 이전 러닝(리스트 선호)과도 충돌하지 않습니다.
가능한 대안:
- private final List<FileJpaEntity> data = Collections.synchronizedList(new ArrayList<>()); + private final List<FileJpaEntity> data = new java.util.concurrent.CopyOnWriteArrayList<>();
37-41: contains 기반 필터의 불필요한 O(n*m) 비용 제거테스트 픽스처라도 ids가 커질 수 있으면 Set으로 변환해 비용 줄이는 것이 안전합니다.
- return data.stream() - .filter(file -> ids.contains(file.getId())) - .map(FileJpaEntity::toDomain) - .toList(); + java.util.Set<Long> idSet = new java.util.HashSet<>(ids); + return data.stream() + .filter(file -> idSet.contains(file.getId())) + .map(FileJpaEntity::toDomain) + .toList();aics-domain/src/main/java/kgu/developers/domain/file/infrastructure/repository/FileRepositoryImpl.java (1)
32-38: ID 목록 조회 시 입력 순서 보존 필요 여부 확인 및 선택적 정렬 제안Spring Data의 findAllById는 입력 순서를 보장하지 않을 수 있습니다. 호출부가 순서에 의존한다면 재정렬하세요.
- return jpaFileRepository.findAllById(ids) - .stream() - .map(FileJpaEntity::toDomain) - .toList(); + var byId = jpaFileRepository.findAllById(ids).stream() + .map(FileJpaEntity::toDomain) + .collect(java.util.stream.Collectors.toMap(FileModel::getId, java.util.function.Function.identity(), (a,b)->a, java.util.LinkedHashMap::new)); + return ids.stream() + .map(byId::get) + .filter(java.util.Objects::nonNull) + .toList();추가 import:
import java.util.stream.Collectors; import java.util.function.Function;
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (38)
aics-admin/src/main/java/kgu/developers/admin/file/application/FileAdminFacade.java(2 hunks)aics-admin/src/testFixtures/java/carousel/application/CarouselAdminFacadeTest.java(2 hunks)aics-admin/src/testFixtures/java/lab/application/LabAdminFacadeTest.java(0 hunks)aics-api/src/main/java/kgu/developers/api/club/presentation/response/ClubDetailResponse.java(0 hunks)aics-api/src/main/java/kgu/developers/api/club/presentation/response/ClubListResponse.java(0 hunks)aics-api/src/testFixtures/java/carousel/application/CarouselFacadeTest.java(0 hunks)aics-api/src/testFixtures/java/lab/application/LabFacadeTest.java(0 hunks)aics-domain/src/main/java/kgu/developers/domain/carousel/application/command/CarouselCommandService.java(2 hunks)aics-domain/src/main/java/kgu/developers/domain/carousel/domain/Carousel.java(2 hunks)aics-domain/src/main/java/kgu/developers/domain/carousel/infrastructure/CarouselRepositoryImpl.java(3 hunks)aics-domain/src/main/java/kgu/developers/domain/carousel/infrastructure/JpaCarouselRepository.java(1 hunks)aics-domain/src/main/java/kgu/developers/domain/carousel/infrastructure/entity/CarouselJpaEntity.java(1 hunks)aics-domain/src/main/java/kgu/developers/domain/club/domain/Club.java(0 hunks)aics-domain/src/main/java/kgu/developers/domain/file/application/command/FileCommandService.java(2 hunks)aics-domain/src/main/java/kgu/developers/domain/file/application/query/FileQueryService.java(2 hunks)aics-domain/src/main/java/kgu/developers/domain/file/application/response/FilePathResponse.java(2 hunks)aics-domain/src/main/java/kgu/developers/domain/file/domain/FileEntity.java(0 hunks)aics-domain/src/main/java/kgu/developers/domain/file/domain/FileModel.java(1 hunks)aics-domain/src/main/java/kgu/developers/domain/file/domain/FileRepository.java(1 hunks)aics-domain/src/main/java/kgu/developers/domain/file/infrastructure/FileRepositoryImpl.java(0 hunks)aics-domain/src/main/java/kgu/developers/domain/file/infrastructure/JpaFileRepository.java(0 hunks)aics-domain/src/main/java/kgu/developers/domain/file/infrastructure/entity/FileJpaEntity.java(1 hunks)aics-domain/src/main/java/kgu/developers/domain/file/infrastructure/properties/FilePathProperties.java(1 hunks)aics-domain/src/main/java/kgu/developers/domain/file/infrastructure/repository/FileRepositoryImpl.java(1 hunks)aics-domain/src/main/java/kgu/developers/domain/file/infrastructure/repository/FileStorageService.java(1 hunks)aics-domain/src/main/java/kgu/developers/domain/file/infrastructure/repository/FileStorageServiceImpl.java(2 hunks)aics-domain/src/main/java/kgu/developers/domain/file/infrastructure/repository/JpaFileRepository.java(1 hunks)aics-domain/src/main/java/kgu/developers/domain/file/infrastructure/repository/QueryFileRepository.java(2 hunks)aics-domain/src/main/java/kgu/developers/domain/post/application/query/PostQueryService.java(2 hunks)aics-domain/src/main/java/kgu/developers/domain/post/application/response/PostDetailResponse.java(2 hunks)aics-domain/src/testFixtures/java/carousel/application/CarouselCommandServiceTest.java(3 hunks)aics-domain/src/testFixtures/java/carousel/application/CarouselQueryServiceTest.java(0 hunks)aics-domain/src/testFixtures/java/carousel/domain/CarouselDomainTest.java(3 hunks)aics-domain/src/testFixtures/java/lab/application/LabCommandServiceTest.java(0 hunks)aics-domain/src/testFixtures/java/lab/application/LabQueryServiceTest.java(0 hunks)aics-domain/src/testFixtures/java/lab/domain/LabDomainTest.java(0 hunks)aics-domain/src/testFixtures/java/mock/repository/FakeCarouselRepository.java(1 hunks)aics-domain/src/testFixtures/java/mock/repository/FakeFileRepository.java(1 hunks)
💤 Files with no reviewable changes (13)
- aics-domain/src/main/java/kgu/developers/domain/club/domain/Club.java
- aics-api/src/main/java/kgu/developers/api/club/presentation/response/ClubDetailResponse.java
- aics-domain/src/testFixtures/java/lab/domain/LabDomainTest.java
- aics-api/src/main/java/kgu/developers/api/club/presentation/response/ClubListResponse.java
- aics-domain/src/testFixtures/java/lab/application/LabCommandServiceTest.java
- aics-api/src/testFixtures/java/carousel/application/CarouselFacadeTest.java
- aics-admin/src/testFixtures/java/lab/application/LabAdminFacadeTest.java
- aics-domain/src/testFixtures/java/lab/application/LabQueryServiceTest.java
- aics-api/src/testFixtures/java/lab/application/LabFacadeTest.java
- aics-domain/src/main/java/kgu/developers/domain/file/domain/FileEntity.java
- aics-domain/src/main/java/kgu/developers/domain/file/infrastructure/FileRepositoryImpl.java
- aics-domain/src/main/java/kgu/developers/domain/file/infrastructure/JpaFileRepository.java
- aics-domain/src/testFixtures/java/carousel/application/CarouselQueryServiceTest.java
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2024-12-03T07:09:18.535Z
Learnt from: minjo-on
PR: kgu-developers/aics-server#125
File: aics-domain/src/testFixtures/java/mock/FakeCommentRepository.java:41-45
Timestamp: 2024-12-03T07:09:18.535Z
Learning: `aics-domain/src/testFixtures/java/mock/FakeCommentRepository.java`의 `findById` 메서드에서는 데이터가 확장될 가능성이 없고 성능 차이가 크지 않으므로 Map을 사용하지 않고 List를 사용하는 것이 바람직합니다.
Applied to files:
aics-domain/src/testFixtures/java/mock/repository/FakeFileRepository.java
🧬 Code graph analysis (4)
aics-domain/src/main/java/kgu/developers/domain/file/infrastructure/repository/FileRepositoryImpl.java (1)
aics-domain/src/main/java/kgu/developers/domain/file/infrastructure/repository/QueryFileRepository.java (1)
Repository(13-27)
aics-domain/src/main/java/kgu/developers/domain/carousel/infrastructure/entity/CarouselJpaEntity.java (1)
aics-domain/src/main/java/kgu/developers/domain/carousel/domain/Carousel.java (1)
Getter(8-46)
aics-domain/src/main/java/kgu/developers/domain/file/infrastructure/entity/FileJpaEntity.java (1)
aics-domain/src/main/java/kgu/developers/domain/file/domain/FileModel.java (1)
Getter(9-47)
aics-domain/src/main/java/kgu/developers/domain/carousel/domain/Carousel.java (1)
aics-domain/src/main/java/kgu/developers/domain/post/domain/Post.java (1)
Getter(12-81)
🔇 Additional comments (22)
aics-domain/src/main/java/kgu/developers/domain/file/infrastructure/properties/FilePathProperties.java (1)
1-1: 패키지 구조 개선이 잘 적용되었습니다.인프라스트럭처를
repository와properties로 명확히 분리한 구조 개선이 적절합니다.aics-domain/src/main/java/kgu/developers/domain/file/infrastructure/repository/FileStorageService.java (1)
1-1: 패키지 구조 개선이 잘 적용되었습니다.FileStorageService를 repository 패키지로 이동하여 책임을 명확히 분리한 점이 좋습니다.
aics-domain/src/main/java/kgu/developers/domain/file/application/response/FilePathResponse.java (1)
6-6: FileEntity에서 FileModel로의 전환이 적절합니다.도메인 모델과 JPA 엔티티의 분리를 통해 도메인의 순수성을 유지하는 좋은 리팩토링입니다.
Also applies to: 18-18
aics-domain/src/main/java/kgu/developers/domain/carousel/domain/Carousel.java (2)
19-33: 팩토리 메소드가 잘 구현되었습니다.
create메소드에서 시간 필드를 적절히 초기화하고 있습니다.
39-41: 새로 추가된 updateLink 메소드가 좋습니다.기존 메소드들과 일관성 있는 구현입니다.
aics-domain/src/testFixtures/java/mock/repository/FakeCarouselRepository.java (1)
20-28: 테스트 저장소가 새로운 도메인 모델에 맞게 잘 업데이트되었습니다.새로운 생성자 시그니처에 맞춰 적절히 구현되었습니다.
aics-domain/src/main/java/kgu/developers/domain/file/domain/FileModel.java (2)
25-39: 팩토리 메소드와 암호화 로직이 잘 구현되었습니다.파일 크기를 읽기 가능한 형태로 변환하고 민감한 정보를 암호화하는 로직이 적절합니다.
41-46: 파일 크기 변환 로직이 잘 구현되었습니다.단위별로 적절한 형식을 제공하는 유틸리티 메소드입니다.
aics-domain/src/main/java/kgu/developers/domain/post/application/response/PostDetailResponse.java (1)
8-8: FileEntity에서 FileModel로의 전환이 잘 적용되었습니다.도메인 모델 분리 작업에 맞춰 적절히 업데이트되었습니다.
Also applies to: 71-77
aics-admin/src/testFixtures/java/carousel/application/CarouselAdminFacadeTest.java (1)
19-19: 테스트가 새로운 FileModel API에 맞게 잘 업데이트되었습니다.MIME 타입을 포함한 새로운 팩토리 메소드 시그니처에 적절히 대응했습니다.
Also applies to: 51-57
aics-domain/src/main/java/kgu/developers/domain/file/infrastructure/repository/JpaFileRepository.java (1)
7-8: LGTM표준 JpaRepository 선언으로 적절합니다.
aics-domain/src/main/java/kgu/developers/domain/post/application/query/PostQueryService.java (1)
44-49: LGTMFileModel로의 치환 및 null 허용 흐름에 맞춘 응답 변환이 자연스럽습니다.
PostDetailResponse.from(post, author, file, ... )가 file == null을 안전하게 처리하는지 한 번만 확인 부탁드립니다.
aics-domain/src/main/java/kgu/developers/domain/carousel/infrastructure/CarouselRepositoryImpl.java (1)
20-22: 도메인-엔티티 매핑 사용 방식 적절합니다.toEntity → save → toDomain 흐름이 명확하고, 조회 시 스트림 매핑도 깔끔합니다. 추가 이슈 없습니다.
Also applies to: 32-35, 40-41
aics-domain/src/main/java/kgu/developers/domain/carousel/infrastructure/JpaCarouselRepository.java (1)
9-11: 전환 완료 확인: 엔티티 기반 리포지토리 시그니처 적절.CarouselJpaEntity로의 전환과 파인더 메서드 시그니처가 일관됩니다. 추가 이슈 없습니다.
aics-domain/src/testFixtures/java/carousel/domain/CarouselDomainTest.java (1)
21-25: 정적 팩토리 기반 테스트 전환 LGTM.생성/수정 API 사용이 도메인 계약과 일치합니다.
Also applies to: 37-41, 55-59
aics-domain/src/main/java/kgu/developers/domain/file/infrastructure/repository/QueryFileRepository.java (1)
18-26: 복호화는 응답 계층에서 수행되므로 QueryFileRepository에서 복호화하지 마십시오.검증 결과: FileModel.create(...)에서 physicalPath를 AesUtil.encrypt로 암호화합니다 (aics-domain/src/main/java/kgu/developers/domain/file/domain/FileModel.java). QueryFileRepository.findPhysicalPathById는 DB의 암호문을 그대로 반환하며, 복호화는 aics-domain/src/main/java/kgu/developers/domain/file/application/response/FilePathResponse.java의 of/from에서 AesUtil.decrypt로 처리되고 있습니다. 리포지토리에서 즉시 복호화하면 호출자 전반에 영향을 미치므로 설계 변경이 아닌 한 불필요합니다.
Likely an incorrect or invalid review comment.
aics-domain/src/testFixtures/java/mock/repository/FakeFileRepository.java (2)
29-33: LGTM – Optional로 외부 동기화 고려스트림 체이닝 및 toDomain 매핑이 명확합니다. 멀티스레드 접근만 없다면 현재 구현으로 충분합니다.
45-49: LGTM – 쿼리 일관성 유지에 적합물리 경로만 선택적으로 반환하는 계약과 테스트 더블의 책임이 잘 맞습니다.
aics-domain/src/main/java/kgu/developers/domain/file/infrastructure/repository/FileRepositoryImpl.java (4)
13-18: 구성/의존성 주입 적절@repository + @requiredargsconstructor 조합 및 역할 분리가 명확합니다.
19-24: save 매핑 경로 명확toEntity → save → toDomain 흐름이 깔끔합니다. 도메인-영속 경계가 잘 드러납니다.
업서트 시 id 존재/부재 처리(toEntity에서 id 보존 여부)만 한번 확인 부탁드려요.
26-30: 단건 조회 매핑 적절Optional 체인과 매핑이 간결합니다.
40-43: 읽기 최적화 위임 👍필드만 필요한 케이스를 QueryDSL 리포지토리로 위임한 결정이 좋습니다.
JangYeongHu
left a comment
There was a problem hiding this comment.
LGTM 👍👍
중요한 이슈는 코드 래빗이 다 짚어준 것 같아서 리뷰랑 같이 확인해주시면 좋을 것 같습니다
dkdltm221
left a comment
There was a problem hiding this comment.
LGTM👍👍
시간 촉박하신데 고생많으셧습니다. 궁금증은 영후님 질문과 동일합니다.
Summary
File 및 Carousel의 도메인과 JPA 엔티티를 분리하고 관련 테스트 코드를 수정하였습니다.*
Tasks
To Reviewer
다소 바쁘게 작업하다보니 놓친 부분이 있을 수 있어요..
혹시 그런게 있으면 편하게 리뷰 남겨주세요~
Screenshot
(없을 경우 삭제) 작업한 내용에 대한 스크린샷을 첨부해주세요.